home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / mkmsgsrc.zip / MKMSGSQU.PAS < prev    next >
Pascal/Delphi Source File  |  1992-09-19  |  43KB  |  1,663 lines

  1. Unit MKMsgSqu;
  2.  
  3. {$I MKB.Def}
  4. {$A-}
  5. Interface
  6.  
  7. Uses MKGlobT, MKMsgAbs;
  8.  
  9.  
  10.  
  11. Const
  12.   SqHdrId = $AFAE4453;
  13.   SqLinkNext = 0;
  14.   SqLinkPrev = 1;
  15.   SqNullFrame = 0;
  16.   SqFrameMsg = 0;
  17.   SqFrameFree = 1;
  18.   SqFrameRLE = 2;
  19.   SqFrameLZW = 3;
  20.   SqFromSize = 36;
  21.   SqToSize = 36;
  22.   SqSubjSize = 72;
  23.   SqMaxReply = 10;
  24.  
  25.  
  26. Type SqBaseType = Record
  27.   Len: Word; {Length of this record}
  28.   Rsvd1: Word; {Future use}
  29.   NumMsg: LongInt; {Number of messages}
  30.   HighMsg: LongInt; {Highest msg}
  31.   SkipMsg: LongInt; {# of msgs to keep in beginning of area}
  32.   HighWater: LongInt; {High water UMsgId}
  33.   Uid: LongInt; {Next UMsgId}
  34.   Base: String[79]; {Base name of Squish file}
  35.   BeginFrame: LongInt; {Offset of first frame in file}
  36.   LastFrame: LongInt; {Offset of last frame in file}
  37.   FirstFree: LongInt; {Offset of first free frame in file}
  38.   LastFree: LongInt; {Offset of last free frame in file}
  39.   EndFrame: LongInt; {Pointer to end of file}
  40.   MaxMsg: LongInt; {Maximum number of messages}
  41.   KeepDays: Word; {Maximum age of messages}
  42.   SqHdrSize: Word; {Size of frame header}
  43.   Rsvd2: Array[1..124] of Byte; {Future use}
  44.   End;
  45.  
  46.  
  47. Type SqFrameHdrType = Record
  48.   Id: LongInt; {Must equal SqHdrId}
  49.   NextFrame: LongInt; {Next msg frame}
  50.   PrevFrame: LongInt; {Prior msg frame}
  51.   FrameLength: LongInt; {Length of this frame not counting header}
  52.   MsgLength: LongInt; {Length of message}
  53.   ControlLength: LongInt; {Length of control information}
  54.   FrameType: Word; {Type of message frame}
  55.   Rsvd: Word; {Future use}
  56.   End;
  57.  
  58.  
  59. Type SqMsgHdrType = Record
  60.   Attr: LongInt; {Msg attribute}
  61.   MsgFrom: String[SqFromSize - 1]; {Nul Term from name}
  62.   MsgTo: String[SqToSize - 1]; {Nul term to name}
  63.   Subj: String[SqSubjSize - 1]; {Nul term subject}
  64.   Orig: AddrType; {Origin address}
  65.   Dest: AddrType; {Destination address}
  66.   DateWritten: LongInt; {Date/Time msg written}
  67.   DateArrived: LongInt; {Date/Time msg arrived here}
  68.   UtcOffset: Word; {Minutes offset from UTC}
  69.   ReplyTo: LongInt; {Original msg}
  70.   Replies: Array[1..SqMaxReply] of LongInt; {Replies}
  71.   AzDate: String[19]; {AsciiZ "Fido" style date}
  72.   End;
  73.  
  74.  
  75. Type SqIdxType = Record
  76.   Ofs: LongInt; {Offset of frame header}
  77.   UMsgId: LongInt; {Unique message id}
  78.   Hash: LongInt; {Hash of MsgTo name}
  79.   End;
  80.  
  81.  
  82. Const
  83.   SqIdxArraySize = 5200;  {5200}
  84.  
  85. Type SqIdxArrayType = Array[1..SqIdxArraySize] of SqIdxType;
  86.  
  87. Type SqIdxPtrType = ^SqIdxArrayType;
  88.  
  89. Type FreeListType = Record
  90.   FreePos: LongInt;
  91.   FreeSize: LongInt;
  92.   End;
  93.  
  94. Const MaxFree = 500;
  95.  
  96. Type FreeArrayType = Array[1..MaxFree] of FreeListType;
  97.  
  98. Const
  99.   SqBSize: Word = SizeOf(SqBaseType);
  100.   SqFSize: Word = SizeOf(SqFrameHdrType);
  101.   SqMSize: Word = SizeOf(SqMsgHdrType);
  102.   SqISize: Word = SizeOf(SqIdxType);
  103.  
  104.  
  105. Const
  106.   SqTxtBufferSize = 34000;  {34000}
  107.  
  108. Type SqInfoType = Record
  109.   FN: String[80];
  110.   MsgChars: Array[1..SqTxtBufferSize] of Char;
  111.   Error: Word;
  112.   SqdFile: File;
  113.   SqIFile: File;
  114.   SqBase: SqBaseType;
  115.   SqBaseExtra: Array[1..100] of Char;
  116.   SqdOpened: Boolean;
  117.   SqiOpened: Boolean;
  118.   Locked: Boolean;
  119.   FreeLoaded: Boolean;
  120.   HighestFree: Word;
  121.   Frame: SqFrameHdrType;
  122.   MsgHdr: SqMsgHdrType;
  123.   Extra: Array[1..100] of Char;
  124.   TxtCtr: Word;
  125.   MsgDone: Boolean;
  126.   LastSoft: Boolean;
  127.   CurrIdx: Word;
  128.   StrDate: String[8];
  129.   StrTime: String[8];
  130.   CurrentFramePos: LongInt;
  131.   CurrentUID: LongInt;
  132.   SName: String[35];
  133.   SHandle: String[35];
  134.   HName: LongInt;
  135.   HHandle: LongInt;
  136.   End;
  137.  
  138.  
  139. Type SqMsgObj = Object(AbsMsgObj)
  140.   SqInfo: ^SqInfoType;
  141.   SqIdx: ^SqIdxArrayType;
  142.   FreeArray: ^FreeArrayType;
  143.   Constructor Init; {Initialize}
  144.   Destructor Done; Virtual; {Done cleanup and dispose}
  145.   Function  OpenMsgBase: Word; Virtual; {Open message base}
  146.   Function  CloseMsgBase: Word; Virtual; {Close message base}
  147.   Function  CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word; Virtual;
  148.   Function  MsgBaseExists: Boolean; Virtual;
  149.   Procedure SetMsgPath(FN: String); Virtual; {Set filepath and name - no extension}
  150.   Function  SqdOpen: Word; Virtual; {Open squish data file}
  151.   Function  SqiOpen: Word; Virtual; {Open squish index file}
  152.   Procedure SqdClose; Virtual; {Close squish data file}
  153.   Procedure SqiClose; Virtual; {Close squish index file}
  154.   Function  LockMsgBase: Boolean; Virtual; {Lock msg base}
  155.   Function  UnLockMsgBase: Boolean; Virtual; {Unlock msg base}
  156.   Procedure ReadBase; Virtual; {Read base data record}
  157.   Procedure WriteBase; Virtual; {Write base data record}
  158.   Function  GetBeginFrame: LongInt; Virtual; {Get beginning frame pos}
  159.   Function  GetHighWater: LongInt; Virtual; {Get high water umsgid}
  160.   Function  GetHighMsgNum: LongInt; Virtual; {Get highest msg number}
  161.   Procedure ReadFrame(FPos: LongInt); Virtual; {Read frame at FPos}
  162.   Procedure ReadVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); Virtual; {Read frame at FPos into Frame}
  163.   Procedure WriteFrame(FPos: LongInt); Virtual; {Write frame at FPos}
  164.   Procedure WriteVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); Virtual;
  165.   Procedure UnlinkFrame(Var Frame: SqFrameHdrType); Virtual; {Unlink frame from linked list}
  166.   Procedure LinkFrameNext(Var Frame: SqFrameHdrType; OtherFrame: LongInt;
  167.     FramePos: LongInt); Virtual; {Link frame after other frame}
  168.   Procedure KillMsg(MsgNum: LongInt); {Kill msg msgnum}
  169.   Procedure KillExcess; {Kill msg in excess of limit}
  170.   Procedure FindFrame(Var FL: LongInt; Var FramePos: LongInt); Virtual;
  171.   Function  GetNextFrame: LongInt; Virtual; {Get next frame pos}
  172.   Procedure ReadMsgHdr(FPos: LongInt); Virtual; {Read msg hdr for frame at FPos}
  173.   Procedure WriteMsgHdr(FPos: LongInt); Virtual; {Read msg hdr for frame at FPos}
  174.   Procedure WriteText(FPos: LongInt); Virtual; {Write text buffer for frame at Fpos}
  175.   Function  SqHashName(Name: String): LongInt; Virtual; {Convert name to hash value}
  176.   Procedure StartNewMsg; Virtual; {Initialize msg header}
  177.   Function  GetFrom: String; Virtual; {Get message from}
  178.   Function  GetTo: String; Virtual; {Get message to}
  179.   Function  GetSubj: String; Virtual; {Get message subject}
  180.   Procedure SetFrom(Str: String); Virtual; {Set message from}
  181.   Procedure SetTo(Str: String); Virtual; {Set message to}
  182.   Procedure SetSubj(Str: String); Virtual; {Set message subject}
  183.   Procedure SetDate(Str: String); Virtual; {Set message date}
  184.   Procedure SetTime(Str: String); Virtual; {Set message time}
  185.   Function  GetDate: String; Virtual; {Get message date mm-dd-yy}
  186.   Function  GetTime: String; Virtual; {Get message time hh:mm}
  187.   Function  GetRefer: LongInt; Virtual; {Get reply to of current msg}
  188.   Procedure SetRefer(Num: LongInt); Virtual; {Set reply to of current msg}
  189.   Function  GetSeeAlso: LongInt; Virtual; {Get see also msg}
  190.   Procedure SetSeeAlso(Num: LongInt); Virtual; {Set see also msg}
  191.   Procedure ReadText(FPos: LongInt); Virtual;
  192.   Function  GetChar: Char; Virtual;
  193.   Function  GetString(MaxLen: Word): String; Virtual;
  194.   Procedure GetOrig(Var Addr: AddrType); Virtual;
  195.   Procedure SetOrig(Var Addr: AddrType); Virtual;
  196.   Procedure GetDest(Var Addr: AddrType); Virtual;
  197.   Procedure SetDest(Var Addr: AddrType); Virtual;
  198.   Function  EOM: Boolean; Virtual;
  199.   Function  WasWrap: Boolean; Virtual;
  200.   Procedure InitText; Virtual;
  201.   Procedure DoString(Str: String); Virtual; {Add string to message text}
  202.   Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
  203.   Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
  204.   Function  WriteMsg: Word; Virtual; {Write msg to msg base}
  205.   Procedure ReadIdx; Virtual;
  206.   Procedure WriteIdx; Virtual;
  207.   Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seeks to 1st msg >= MsgNum}
  208.   Function  GetMsgNum: LongInt; Virtual;
  209.   Procedure SeekNext; Virtual;
  210.   Procedure SeekPrior; Virtual;
  211.   Function  SeekFound: Boolean; Virtual;
  212.   Function  GetIdxFramePos: LongInt; Virtual;
  213.   Function  GetIdxHash: LongInt; Virtual;
  214.   Function  IsLocal: Boolean; Virtual; {Is current msg local}
  215.   Function  IsCrash: Boolean; Virtual; {Is current msg crash}
  216.   Function  IsKillSent: Boolean; Virtual; {Is current msg kill sent}
  217.   Function  IsSent: Boolean; Virtual; {Is current msg sent}
  218.   Function  IsFAttach: Boolean; Virtual; {Is current msg file attach}
  219.   Function  IsReqRct: Boolean; Virtual; {Is current msg request receipt}
  220.   Function  IsReqAud: Boolean; Virtual; {Is current msg request audit}
  221.   Function  IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
  222.   Function  IsFileReq: Boolean; Virtual; {Is current msg a file request}
  223.   Function  IsRcvd: Boolean; Virtual; {Is current msg received}
  224.   Function  IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
  225.   Function  IsDeleted: Boolean; Virtual; {Is current msg deleted}
  226.   Procedure SetAttr(St: Boolean; Mask: LongInt); Virtual; {Set attribute}
  227.   Procedure SetLocal(St: Boolean); Virtual; {Set local status}
  228.   Procedure SetRcvd(St: Boolean); Virtual; {Set received status}
  229.   Procedure SetPriv(St: Boolean); Virtual; {Set priveledge vs public status}
  230.   Procedure SetCrash(St: Boolean); Virtual; {Set crash netmail status}
  231.   Procedure SetKillSent(St: Boolean); Virtual; {Set kill/sent netmail status}
  232.   Procedure SetSent(St: Boolean); Virtual; {Set sent netmail status}
  233.   Procedure SetFAttach(St: Boolean); Virtual; {Set file attach status}
  234.   Procedure SetReqRct(St: Boolean); Virtual; {Set request receipt status}
  235.   Procedure SetReqAud(St: Boolean); Virtual; {Set request audit status}
  236.   Procedure SetRetRct(St: Boolean); Virtual; {Set return receipt status}
  237.   Procedure SetFileReq(St: Boolean); Virtual; {Set file request status}
  238.   Procedure MsgStartUp; Virtual; {Set up message}
  239.   Procedure MsgTxtStartUp; Virtual; {Set up for msg text}
  240.   Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
  241.   Function  GetSubArea: Word; Virtual; {Get sub area number}
  242.   Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
  243.   Procedure DeleteMsg; Virtual; {Delete current message}
  244.   Procedure LoadFree; Virtual; {Load freelist into memory}
  245.   Function  NumberOfMsgs: LongInt; Virtual; {Number of messages}
  246.   Procedure SetEcho(ES: Boolean); Virtual; {Set echo status}
  247.   Function  IsEchoed: Boolean; Virtual; {Is current msg unmoved echomail msg}
  248.   Function  GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
  249.   Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
  250.   Function  GetMsgLoc: LongInt; Virtual; {To allow reseeking to message}
  251.   Procedure SetMsgLoc(ML: LongInt); Virtual; {Reseek to message}
  252.   Function  IdxHighest: LongInt; Virtual; { *** }
  253.   Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
  254.   Procedure YoursNext; Virtual; {Seek next your mail}
  255.   Function  YoursFound: Boolean; Virtual; {Message found}
  256.   Function  GetMsgDisplayNum: LongInt; Virtual; {Get msg number to display}
  257.   Function  GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
  258.   Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
  259.   End;
  260.  
  261. Type SqMsgPtr = ^SqMsgObj;
  262.  
  263. Implementation
  264.  
  265. Uses MKFile, MKString, MKDos
  266. {$IFDEF WINDOWS}
  267. ,WinDos;
  268. {$ELSE}
  269. ,Dos;
  270. {$ENDIF}
  271.  
  272.  
  273. Const
  274.   SqMsgPriv =   $00001;
  275.   SqMsgCrash =  $00002;
  276.   SqMsgRcvd =   $00004;
  277.   SqMsgSent =   $00008;
  278.   SqMsgFile =   $00010;
  279.   SqMsgFwd =    $00020;
  280.   SqMsgOrphan = $00040;
  281.   SqMsgKill =   $00080;
  282.   SqMsgLocal =  $00100;
  283.   SqMsgHold =   $00200;
  284.   SqMsgXX2 =    $00400;
  285.   SqMsgFreq =   $00800;
  286.   SqMsgRrq =    $01000;
  287.   SqMsgCpt =    $02000;
  288.   SqMsgArq =    $04000;
  289.   SqMsgUrg =    $08000;
  290.   SqMsgScanned= $10000;
  291.  
  292.  
  293. Constructor SqMsgObj.Init;
  294.   Begin
  295.   New(SqInfo);
  296.   New(SqIdx);
  297.   New(FreeArray);
  298.   If ((SqInfo = nil) or (SqIdx = nil) or (FreeArray = nil)) Then
  299.     Begin
  300.     If SqInfo <> Nil Then
  301.       Dispose(SqInfo);
  302.     If SqIdx <> Nil Then
  303.       Dispose(SqIdx);
  304.     If FreeArray <> Nil Then
  305.       Dispose(FreeArray);
  306.     Fail;
  307.     Exit;
  308.     End;
  309.   SqInfo^.SqdOpened := False;
  310.   SqInfo^.SqiOpened := False;
  311.   SqInfo^.FN := '';
  312.   SqInfo^.Error := 0;
  313.   SqInfo^.Locked := False;
  314.   SqInfo^.FreeLoaded := False;
  315.   End;
  316.  
  317.  
  318. Destructor SqMsgObj.Done;
  319.   Begin
  320.   If SqInfo^.SqdOpened Then
  321.     SqdClose;
  322.   If SqInfo^.SqiOpened Then
  323.     SqiClose;
  324.   Dispose(FreeArray);
  325.   Dispose(SqInfo);
  326.   Dispose(SqIdx);
  327.   End;
  328.  
  329.  
  330. Procedure SqMsgObj.SetMsgPath(FN: String);
  331.   Begin
  332.   SqInfo^.FN := FExpand(FN);
  333.   If Pos('.', SqInfo^.FN) > 0 Then
  334.     SqInfo^.FN := Copy(SqInfo^.FN,1,Pos('.', SqInfo^.FN) - 1);
  335.   End;
  336.  
  337.  
  338. Function SqMsgObj.OpenMsgBase: Word;
  339.   Begin
  340.   If SqiOpen = 0 Then
  341.     Begin
  342.     OpenMsgBase := SqdOpen;
  343.     ReadIdx;
  344.     End
  345.   Else
  346.     OpenMsgBase := 100;
  347.   End;
  348.  
  349.  
  350. Function SqMsgObj.SqdOpen: Word;
  351.   Var
  352.     NumRead: Word;
  353.  
  354.   Begin
  355.   If Not SqInfo^.SqdOpened Then
  356.     Begin
  357.     Assign(SqInfo^.SqdFile, SqInfo^.FN + '.SQD');
  358.     FileMode := fmReadWrite + fmDenyNone;
  359.     If Not shReset(SqInfo^.SqdFile, 1) Then
  360.       SqdOpen := FileError
  361.     Else
  362.       Begin
  363.       SqInfo^.SqdOpened := True;
  364.       SqdOpen := 0;
  365.       If Not shRead(SqInfo^.SqdFile, SqInfo^.SqBase, 2, NumRead) Then
  366.         SqdOpen := FileError
  367.       Else
  368.         Begin
  369.         If SqInfo^.SqBase.Len = 0 Then
  370.           SqInfo^.SqBase.Len := SqBSize;
  371.         If SqInfo^.SqBase.Len > (SizeOf(SqBaseType) + 100) Then
  372.           SqdOpen := 1001
  373.         Else
  374.           Begin
  375.           SqBSize := SqInfo^.SqBase.Len;
  376.           ReadBase;
  377.           End;
  378.         End;
  379.       End;
  380.     End
  381.   Else
  382.     SqdOpen := 0;
  383.   End;
  384.  
  385.  
  386. Function SqMsgObj.SqiOpen: Word;
  387.   Begin
  388.   If Not SqInfo^.SqiOpened Then
  389.     Begin
  390.     Assign(SqInfo^.SqiFile, SqInfo^.FN + '.SQI');
  391.     FileMode := fmReadWrite + fmDenyNone;
  392.     If Not shReset(SqInfo^.SqiFile, SizeOf(SqIdxType)) Then
  393.       SqiOpen := FileError
  394.     Else
  395.       Begin
  396.       SqInfo^.SqiOpened := True;
  397.       SqiOpen := 0;
  398.       End;
  399.     End
  400.   Else
  401.     SqiOpen := 0;
  402.   End;
  403.  
  404.  
  405. Function SqMsgObj.CloseMsgBase: Word;
  406.   Begin
  407.   SqdClose;
  408.   SqiClose;
  409.   CloseMsgBase := 0;
  410.   End;
  411.  
  412.  
  413. Function SqMsgObj.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word;
  414.   Begin
  415.   If Not SqInfo^.SqdOpened Then
  416.     Begin
  417.     FillChar(SqInfo^.SqBase, SizeOf(SqInfo^.SqBase), 0);
  418.     SqInfo^.SqBase.Len := 256;
  419.     SqInfo^.SqBase.SqHdrSize := SqFSize;
  420.     SqInfo^.SqBase.UID := 1;
  421.     SqInfo^.SqBase.NumMsg := 0;
  422.     SqInfo^.SqBase.Base := SqInfo^.FN;
  423.     Str2Az(SqInfo^.FN, 78, SqInfo^.SqBase.Base);
  424.     SqInfo^.SqBase.MaxMsg := MaxMsg;
  425.     SqInfo^.SqBase.KeepDays := MaxDays;
  426.     SqInfo^.SqBase.EndFrame := SqInfo^.SqBase.Len;
  427.     CreateMsgBase := SaveFile(SqInfo^.FN + '.SQD', SqInfo^.SqBase, SqInfo^.SqBase.Len);
  428.     If SaveFile(SqInfo^.FN + '.SQI', SqInfo^.SqBase, 0) = 0 Then;
  429.     If SaveFile(SqInfo^.FN + '.SQL', SqInfo^.SqBase, 0) = 0 Then;
  430.     End
  431.   Else
  432.     CreateMsgBase := 176;
  433.   End;
  434.  
  435.  
  436. Function SqMsgObj.MsgBaseExists: Boolean;
  437.   Begin
  438.   MsgBaseExists :=  FileExist(SqInfo^.FN + '.SQD');
  439.   End;
  440.  
  441.  
  442. Procedure SqMsgObj.SqdClose;
  443.   Begin
  444.   If SqInfo^.SqdOpened Then
  445.     Close(SqInfo^.SqdFile);
  446.   If IOResult <> 0 Then;
  447.   End;
  448.  
  449.  
  450. Function SqMsgObj.LockMsgBase: Boolean; {Lock msg base}
  451.   Begin
  452.   If Not SqInfo^.Locked Then
  453.     Begin
  454.     SqInfo^.Locked := shLock(SqInfo^.SqdFile, 0, 1) = 0;
  455.     LockMsgBase := SqInfo^.Locked;
  456.     ReadBase;
  457.     ReadIdx;
  458.     SqInfo^.FreeLoaded := False;
  459.     End;
  460.   End;
  461.  
  462.  
  463. Function SqMsgObj.UnLockMsgBase: Boolean; {Unlock msg base}
  464.   Begin
  465.   If SqInfo^.Locked Then
  466.     Begin
  467.     WriteBase;
  468.     WriteIdx;
  469.     SqInfo^.Locked := Not UnLockFile(SqInfo^.SqdFile, 0, 1) < 2;
  470.     UnLockMsgBase := Not SqInfo^.Locked;
  471.     End;
  472.   End;
  473.  
  474.  
  475. Procedure SqMsgObj.SqiClose;
  476.   Begin
  477.   If SqInfo^.SqiOpened Then
  478.     Close(SqInfo^.SqiFile);
  479.   If IoResult <> 0 Then;
  480.   End;
  481.  
  482.  
  483. Procedure SqMsgObj.ReadBase;
  484.   Var
  485.     NumRead: Word;
  486.  
  487.   Begin
  488.   Seek(SqInfo^.SqdFile, 0);
  489.   If Not shRead(SqInfo^.SqdFile, SqInfo^.SqBase, SqBSize, NumRead) Then
  490.     SqInfo^.Error := FileError;
  491.   If SqInfo^.SqBase.SqHdrSize = 0 Then
  492.     SQInfo^.SqBase.SqHdrSize := SqFSize;
  493.   SqFSize := SqInfo^.SqBase.SqHdrSize;
  494.   End;
  495.  
  496.  
  497. Procedure SqMsgObj.WriteBase;
  498.   Begin
  499.   Seek(SqInfo^.SqdFile, 0);
  500.   If Not shWrite(SqInfo^.SqdFile, SqInfo^.SqBase, SQBSize) Then
  501.     SqInfo^.Error := FileError;
  502.   End;
  503.  
  504.  
  505. Procedure SqMsgObj.StartNewMsg; {Initialize msg header}
  506.   Begin
  507.   FillChar(SqInfo^.MsgHdr, SizeOf(SqInfo^.MsgHdr), 0);
  508.   FillChar(SqInfo^.Frame, SizeOf(SqInfo^.Frame), 0);
  509.   SqInfo^.TxtCtr := 0;
  510.   SqInfo^.StrDate := '';
  511.   SqInfo^.StrTime := '';
  512.   End;
  513.  
  514.  
  515. Function SqMsgObj.GetFrom: String; {Get message from}
  516.   Begin
  517.   GetFrom := Az2Str(SqInfo^.MsgHdr.MsgFrom, 35);
  518.   End;
  519.  
  520.  
  521. Function SqMsgObj.GetTo: String; {Get message to}
  522.   Begin
  523.   GetTo := Az2Str(SqInfo^.MsgHdr.MsgTo, 35);
  524.   End;
  525.  
  526.  
  527. Function SqMsgObj.GetSubj: String; {Get message subject}
  528.   Begin
  529.   GetSubj := Az2Str(SqInfo^.MsgHdr.Subj, 72);
  530.   End;
  531.  
  532.  
  533. Procedure SqMsgObj.SetFrom(Str: String); {Set message from}
  534.   Begin
  535.   Str2Az(Str, 35, SqInfo^.MsgHdr.MsgFrom);
  536.   End;
  537.  
  538.  
  539. Procedure SqMsgObj.SetTo(Str: String); {Set message to}
  540.   Begin
  541.   Str2Az(Str,35, SqInfo^.MsgHdr.MsgTo);
  542.   End;
  543.  
  544.  
  545. Procedure SqMsgObj.SetSubj(Str: String); {Set message subject}
  546.   Begin
  547.   Str2Az(Str,72, SqInfo^.MSgHdr.Subj);
  548.   End;
  549.  
  550.  
  551. Function SqMsgObj.GetDate: String; {Get message date mm-dd-yy}
  552.   Var
  553.     TmpDate: LongInt;
  554.  
  555.   Begin
  556.   TmpDate := (SqInfo^.MsgHdr.DateWritten shr 16) +
  557.    ((SqInfo^.MsgHdr.DateWritten and $ffff) shl 16);
  558.   GetDate := DateStr(TmpDate);
  559.   End;
  560.  
  561.  
  562. Function SqMsgObj.GetTime: String; {Get message time hh:mm}
  563.   Var
  564.     TmpDate: LongInt;
  565.  
  566.   Begin
  567.   TmpDate := (SqInfo^.MsgHdr.DateWritten shr 16) +
  568.    ((SqInfo^.MsgHdr.DateWritten and $ffff) shl 16);
  569.   GetTime := TimeStr(TmpDate);
  570.   End;
  571.  
  572.  
  573. Procedure SqMsgObj.SetDate(Str: String);
  574.   Begin
  575.   SqInfo^.StrDate := Copy(Str,1,8);
  576.   End;
  577.  
  578.  
  579. Procedure SqMsgObj.SetTime(Str: String);
  580.   Begin
  581.   SqInfo^.StrTime := Copy(Str,1,8);
  582.   End;
  583.  
  584.  
  585. Procedure SqMsgObj.GetOrig(Var Addr: AddrType);
  586.   Begin
  587.   Addr := SqInfo^.MsgHdr.Orig;
  588.   End;
  589.  
  590.  
  591. Procedure SqMsgObj.SetOrig(Var Addr: AddrType);
  592.   Begin
  593.   SqInfo^.MsgHdr.Orig := Addr;
  594.   End;
  595.  
  596.  
  597. Procedure SqMsgObj.GetDest(Var Addr: AddrType);
  598.   Begin
  599.   Addr := SqInfo^.MsgHdr.Dest;
  600.   End;
  601.  
  602.  
  603. Procedure SqMsgObj.SetDest(Var Addr: AddrType);
  604.   Begin
  605.   SqInfo^.MsgHdr.Dest := Addr;
  606.   End;
  607.  
  608.  
  609. Function SqMsgObj.SqHashName(Name: String): LongInt;
  610.   Var
  611.     Hash: LongInt;
  612.     Tmp: LongInt;
  613.     Counter: Word;
  614.  
  615.   Begin
  616.   Hash := 0;
  617.   Counter := 1;
  618.   While Counter <= Length(Name) Do
  619.     Begin
  620.     Hash := (Hash shl 4) + Ord(LoCase(Name[Counter]));
  621.     Tmp := Hash and $F0000000;
  622.     If (Tmp <> 0) Then
  623.       Hash := (Hash or (Tmp shr 24)) or Tmp;
  624.     Inc(Counter);
  625.     End;
  626.   SqHashName := Hash and $7fffffff;
  627.   End;
  628.  
  629.  
  630. Procedure SqMsgObj.ReadFrame(FPos: LongInt); {Read frame at FPos}
  631.   Begin
  632.   ReadVarFrame(SqInfo^.Frame, FPos);
  633.   End;
  634.  
  635.  
  636. Procedure SqMsgObj.ReadVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); {Read frame at FPos}
  637.   Var
  638.     NumRead: Word;
  639.  
  640.   Begin
  641.   Seek(SqInfo^.SqdFile, FPos);
  642.   SqInfo^.Error := IoResult;
  643.   If SqInfo^.Error = 0 Then
  644.     Begin
  645.     If Not shRead(SqInfo^.SqdFile, Frame, SizeOf(SqFrameHdrType), NumRead) Then
  646.       SqInfo^.Error := FileError;
  647.     End;
  648.   End;
  649.  
  650.  
  651. Procedure SqMsgObj.WriteFrame(FPos: LongInt); {Read frame at FPos}
  652.   Begin
  653.   WriteVarFrame(SqInfo^.Frame, FPos);
  654.   End;
  655.  
  656.  
  657. Procedure SqMsgObj.WriteVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); {Write frame at FPos}
  658.   Begin
  659.   Seek(SqInfo^.SqdFile, FPos);
  660.   SqInfo^.Error := IoResult;
  661.   If SqInfo^.Error = 0 Then
  662.     Begin
  663.     If Not shWrite(SqInfo^.SqdFile, Frame, SizeOf(SqFrameHdrType)) Then
  664.       SqInfo^.Error := FileError;
  665.     End;
  666.   End;
  667.  
  668.  
  669.  
  670. Procedure SqMsgObj.UnlinkFrame(Var Frame: SqFrameHdrType);
  671.   Var
  672.     TmpFrame: SqFrameHdrType;
  673.  
  674.   Begin
  675.   If Frame.PrevFrame <> 0 Then
  676.     Begin
  677.     ReadVarFrame(TmpFrame, Frame.PrevFrame);
  678.     TmpFrame.NextFrame := Frame.NextFrame;
  679.     WriteVarFrame(TmpFrame, Frame.PrevFrame);
  680.     End;
  681.   If Frame.NextFrame <> 0 Then
  682.     Begin
  683.     ReadVarFrame(TmpFrame, Frame.NextFrame);
  684.     TmpFrame.PrevFrame := Frame.PrevFrame;
  685.     WriteVarFrame(TmpFrame, Frame.NextFrame);
  686.     End;
  687.   End;
  688.  
  689.  
  690. Procedure SqMsgObj.LoadFree;
  691.   Var
  692.     i: Word;
  693.     TmpFrame: SqFrameHdrType;
  694.     TmpPos: LongInt;
  695.  
  696.   Begin
  697.   For i := 1 to MaxFree Do
  698.     Begin
  699.     FreeArray^[i].FreePos := 0;
  700.     FreeArray^[i].FreeSize := 0;
  701.     End;
  702.   SqInfo^.FreeLoaded := True;
  703.   i := 0;
  704.   TmpPos := SqInfo^.SqBase.FirstFree;
  705.   While ((TmpPos <> 0) and (i < MaxFree)) Do
  706.     Begin
  707.     ReadVarFrame(TmpFrame, TmpPos);
  708.     Inc(i);
  709.     FreeArray^[i].FreeSize := TmpFrame.FrameLength;
  710.     FreeArray^[i].FreePos := TmpPos;
  711.     TmpPos := TmpFrame.NextFrame;
  712.     End;
  713.   SqInfo^.HighestFree := i;
  714.   End;
  715.  
  716.  
  717. Procedure SqMsgObj.FindFrame(Var FL: LongInt; Var FramePos: LongInt);
  718.   Var
  719.     TmpFrame: SqFrameHdrType;
  720.     BestFoundPos: LongInt;
  721.     BestFoundSize: LongInt;
  722.     BestIdx: Word;
  723.     i: Word;
  724.  
  725.   Begin
  726.   If Not SqInfo^.FreeLoaded Then
  727.     LoadFree;
  728.   BestFoundPos := 0;
  729.   BestFoundSize := 0;
  730.   For i := 1 to SqInfo^.HighestFree Do
  731.     Begin
  732.     If (FreeArray^[i].FreeSize > FL) Then
  733.       Begin
  734.       If ((BestFoundSize = 0) or (FreeArray^[i].FreeSize < BestFoundSize)) Then
  735.         Begin
  736.         BestFoundSize := FreeArray^[i].FreeSize;
  737.         BestFoundPos := FreeArray^[i].FreePos;
  738.         BestIdx := i;
  739.         End;
  740.       End
  741.     End;
  742.   FramePos := BestFoundPos;
  743.   If FramePos <> 0 Then
  744.     Begin
  745.     ReadVarFrame(TmpFrame, FramePos);
  746.     FreeArray^[BestIdx].FreePos := 0;
  747.     FreeArray^[BestIdx].FreeSize := 0;
  748.     End;
  749.   If FramePos = 0 Then
  750.     Begin
  751.     FL := 0;
  752.     FramePos := SqInfo^.SqBase.EndFrame;
  753.     End
  754.   Else
  755.     Begin
  756.     UnLinkFrame(TmpFrame);
  757.     If TmpFrame.PrevFrame = 0 Then
  758.       SqInfo^.SqBase.FirstFree := TmpFrame.NextFrame;
  759.     If TmpFrame.NextFrame = 0 Then
  760.       SqInfo^.SqBase.LastFree := TmpFrame.PrevFrame;
  761.     FL := TmpFrame.FrameLength;
  762.     End;
  763.   End;
  764.  
  765.  
  766. Procedure SqMsgObj.LinkFrameNext(Var Frame: SqFrameHdrType; OtherFrame: LongInt;
  767.   FramePos: LongInt);
  768.  
  769.   Var
  770.     TmpFrame: SqFrameHdrType;
  771.  
  772.   Begin
  773.   If OtherFrame <> 0 Then
  774.     Begin
  775.     ReadVarFrame(TmpFrame, OtherFrame);
  776.     TmpFrame.NextFrame := FramePos;
  777.     Frame.PrevFrame := OtherFrame;
  778.     WriteVarFrame(TmpFrame, OtherFrame);
  779.     End;
  780.   End;
  781.  
  782.  
  783. Procedure SqMsgObj.KillMsg(MsgNum: LongInt);
  784.   Var
  785.     i: Word;
  786.     KillPos: LongInt;
  787.     IndexPos: LongInt;
  788.     KillFrame: SqFrameHdrType;
  789.     TmpFrame: SqFrameHdrType;
  790.     CurrMove: LongInt;
  791.     AlreadyLocked: Boolean;
  792.     FreeCtr: Word;
  793.  
  794.   Begin
  795.   AlreadyLocked := SqInfo^.Locked;
  796.   If Not AlreadyLocked Then
  797.     If LockMsgBase Then;
  798.   i := 1;
  799.   While ((i <= SqInfo^.SqBase.NumMsg) and (MsgNum <> SqIdx^[i].UMsgId)) Do
  800.     Inc(i);
  801.   If MsgNum = SqIdx^[i].UMsgId Then
  802.     Begin
  803.     IndexPos := i;
  804.     KillPos := SqIdx^[i].Ofs;
  805.     ReadVarFrame(KillFrame, KillPos);
  806.     If KillFrame.PrevFrame = 0 Then
  807.       SqInfo^.SqBase.BeginFrame := KillFrame.NextFrame;
  808.     If KillFrame.NextFrame = 0 Then
  809.       SqInfo^.SqBase.LastFrame := KillFrame.PrevFrame;
  810.     KillFrame.FrameType := sqFrameFree;
  811.     UnLinkFrame(KillFrame);
  812.     If ((SqInfo^.SqBase.FirstFree = 0) or (SqInfo^.SqBase.LastFree = 0)) Then
  813.       Begin
  814.       SqInfo^.SqBase.FirstFree := KillPos;
  815.       SqInfo^.SqBase.LastFree := KillPos;
  816.       KillFrame.PrevFrame := 0;
  817.       KillFrame.NextFrame := 0;
  818.       End
  819.     Else
  820.       Begin
  821.       KillFrame.NextFrame := 0;
  822.       KillFrame.PrevFrame := SqInfo^.SqBase.LastFree;
  823.       ReadVarFrame(TmpFrame, SqInfo^.SqBase.LastFree);
  824.       TmpFrame.NextFrame := KillPos;
  825.       WriteVarFrame(TmpFrame, SqInfo^.SqBase.LastFree);
  826.       SqInfo^.SqBase.LastFree := KillPos;
  827.       End;
  828.     WriteVarFrame(KillFrame, KillPos);
  829.     FreeCtr := 1;
  830.     While ((FreeCtr < MaxFree) and (FreeArray^[FreeCtr].FreePos <> 0)) Do
  831.       Inc(FreeCtr);
  832.     If FreeArray^[FreeCtr].FreePos = 0 Then
  833.       Begin
  834.       FreeArray^[FreeCtr].FreePos := KillPos;
  835.       FreeArray^[FreeCtr].FreeSize := KillFrame.FrameLength;
  836.       End;
  837.     If FreeCtr > SqInfo^.HighestFree Then
  838.       SqInfo^.HighestFree := FreeCtr;
  839.     Dec(SqInfo^.SqBase.NumMsg);
  840.     Dec(SqInfo^.SqBase.HighMsg);
  841.     CurrMove := IndexPos;
  842.     While CurrMove <= SqInfo^.SqBase.NumMsg Do
  843.       Begin
  844.       SqIdx^[CurrMove] := SqIdx^[CurrMove + 1];
  845.       Inc(CurrMove);
  846.       End;
  847. {    NumMove := SqInfo^.SqBase.NumMsg + 1 - IndexPos;
  848.     NumMove := NumMove * SizeOf(SqIdxType);
  849.     Move(SqIdx^[IndexPos + 1], SqIdx^[IndexPos], NumMove);  }
  850.     End;
  851.   If Not AlreadyLocked Then
  852.     If UnlockMsgBase Then;
  853.   End;
  854.  
  855.  
  856. Procedure SqMsgObj.ReadMsgHdr(FPos: LongInt); {Read msg hdr for frame at FPos}
  857.   Var
  858.     NumRead: Word;
  859.  
  860.   Begin
  861.   Seek(SqInfo^.SqdFile, FPos + SqFSize);
  862.   SqInfo^.Error := IoResult;
  863.   If SqInfo^.Error = 0 Then
  864.     Begin
  865.     If Not shRead(SqInfo^.SqdFile, SqInfo^.MsgHdr, SizeOf(SqMsgHdrType), NumRead) Then
  866.       SqInfo^.Error := FileError;
  867.     End;
  868.   End;
  869.  
  870.  
  871. Procedure SqMsgObj.WriteMsgHdr(FPos: LongInt); {Read msg hdr for frame at FPos}
  872.   Var
  873.     NumRead: Word;
  874.  
  875.   Begin
  876.   Seek(SqInfo^.SqdFile, FPos + SqFSize);
  877.   SqInfo^.Error := IoResult;
  878.   If SqInfo^.Error = 0 Then
  879.     Begin
  880.     If Not shWrite(SqInfo^.SqdFile, SqInfo^.MsgHdr, SizeOf(SqMsgHdrType)) Then
  881.       SqInfo^.Error := FileError;
  882.     End;
  883.   End;
  884.  
  885.  
  886. Procedure SqMsgObj.WriteText(FPos: LongInt); {Write text buffer for frame at Fpos}
  887.   Begin
  888.   Seek(SqInfo^.SqdFile, FPos + SqFSize + SqMSize);
  889.   SqInfo^.Error := IoResult;
  890.   If SqInfo^.Error = 0 Then
  891.     Begin
  892.     If Not shWrite(SqInfo^.SqdFile, SqInfo^.MsgChars, SqInfo^.TxtCtr) Then
  893.       SqInfo^.Error := FileError;
  894.     End;
  895.   End;
  896.  
  897.  
  898. Function SqMsgObj.GetBeginFrame: LongInt; {Get beginning frame pos}
  899.   Begin
  900.   GetBeginFrame := SqInfo^.SqBase.BeginFrame;
  901.   End;
  902.  
  903.  
  904. Function SqMsgObj.GetNextFrame: LongInt; {Get next frame pos}
  905.   Begin
  906.   GetNextFrame := SqInfo^.Frame.NextFrame;
  907.   End;
  908.  
  909.  
  910. Procedure SqMsgObj.ReadText(FPos: LongInt);
  911.   Begin
  912.   Seek(SqInfo^.SqdFile, FPos + SqFSize + SqMSize);
  913.   SqInfo^.Error := IoResult;
  914.   If SqInfo^.Error = 0 Then
  915.     Begin
  916.     If SqInfo^.Frame.MsgLength > SqTxtBufferSize Then
  917.       BlockRead(SqInfo^.SqdFile, SqInfo^.MsgChars, SqTxtBufferSize)
  918.     Else
  919.       BlockRead(SqInfo^.SqdFile, SqInfo^.MsgChars, SqInfo^.Frame.MsgLength);
  920.     SqInfo^.Error := IoResult;
  921.     End;
  922.   SqInfo^.TxtCtr := 1 + SqInfo^.Frame.ControlLength;
  923.   SqInfo^.MsgDone := False;
  924.   SqInfo^.LastSoft := False;
  925.   End;
  926.  
  927.  
  928.  
  929. Procedure SqMsgObj.InitText;
  930.   Begin
  931.   SqInfo^.TxtCtr := 0;
  932.   End;
  933.  
  934.  
  935. Procedure SqMsgObj.DoString(Str: String); {Add string to message text}
  936.   Var
  937.     i: Word;
  938.  
  939.   Begin
  940.   i := 1;
  941.   While i <= Length(Str) Do
  942.     Begin
  943.     DoChar(Str[i]);
  944.     Inc(i);
  945.     End;
  946.   End;
  947.  
  948.  
  949. Procedure SqMsgObj.DoChar(Ch: Char); {Add character to message text}
  950.   Begin
  951.   If SqInfo^.TxtCtr < SqTxtBufferSize Then
  952.     Begin
  953.     Inc(SqInfo^.TxtCtr);
  954.     SqInfo^.MsgChars[SqInfo^.TxtCtr] := ch;
  955.     End;
  956.   End;
  957.  
  958.  
  959. Procedure SqMsgObj.DoStringLn(Str: String); {Add string and newline to msg text}
  960.   Begin
  961.   DoString(Str);
  962.   DoChar(#13);
  963.   End;
  964.  
  965.  
  966.  
  967. Procedure SqMsgObj.KillExcess;
  968.   Var
  969.     AlreadyLocked: Boolean;
  970.  
  971.   Begin
  972.   AlreadyLocked := SqInfo^.Locked;
  973.   If Not AlreadyLocked Then
  974.     If LockMsgBase Then;
  975.   If ((SqInfo^.SqBase.MaxMsg > 0) and
  976.   (SqInfo^.SqBase.MaxMsg > SqInfo^.SqBase.SkipMsg)) Then
  977.     Begin
  978.     While (SqInfo^.SqBase.NumMsg > SqInfo^.SqBase.MaxMsg) Do
  979.       KillMsg(SqIdx^[SqInfo^.SqBase.SkipMsg + 1].UMsgId);
  980.     End;
  981.   If Not AlreadyLocked Then
  982.     If UnlockMsgBase Then;
  983.   End;
  984.  
  985.  
  986. Function SqMsgObj.WriteMsg: Word; {Write msg to msg base}
  987.   Var
  988.     MsgSize: LongInt;
  989.     FrameSize: LongInt;
  990.     FramePos: LongInt;
  991.     TmpFrame: SqFrameHdrType;
  992.     TmpDate: LongInt;
  993.     {$IFDEF WINDOWS}
  994.     TmpDT: TDateTime;
  995.     {$ELSE}
  996.     TmpDT: DateTime;
  997.     {$ENDIF}
  998.     TmpStr: String;
  999.     AlreadyLocked: Boolean;
  1000.  
  1001.   Begin
  1002.   DoChar(#0);
  1003.   TmpDT.Year := Str2Long(Copy(SqInfo^.StrDate,7,2));
  1004.   If TmpDT.Year > 79 Then
  1005.     Inc(TmpDT.Year, 1900)
  1006.   Else
  1007.     Inc(TmpDT.Year, 2000);
  1008.   TmpDT.Month := Str2Long(Copy(SqInfo^.StrDate,1,2));
  1009.   TmpDT.Day := Str2Long(Copy(SqInfo^.StrDate,4,2));
  1010.   TmpDt.Hour := Str2Long(Copy(SqInfo^.StrTime,1,2));
  1011.   TmpDt.Min := Str2Long(Copy(SqInfo^.StrTime, 4,2));
  1012.   TmpDt.Sec := 0;
  1013.   TmpStr := FormattedDate(TmpDT, 'DD NNN YY  ') + Copy(SqInfo^.StrTime,1,5) + ':00';
  1014.   PackTime(TmpDT, TmpDate);
  1015.   SqInfo^.MsgHdr.DateWritten :=  (TmpDate shr 16) + ((TmpDate and $ffff) shl 16);
  1016.   TmpDate := GetDosDate;
  1017.   SqInfo^.MsgHdr.DateArrived := (TmpDate shr 16) + ((TmpDate and $ffff) shl 16);
  1018.   Str2AZ(TmpStr, 20, SqInfo^.MsgHdr.AZDate);
  1019.   AlreadyLocked := SqInfo^.Locked;
  1020.   If Not AlreadyLocked Then
  1021.     If LockMsgBase Then;
  1022.   If SqInfo^.Locked Then
  1023.     Begin
  1024.     MsgSize := SqInfo^.TxtCtr + SqMSize;
  1025.     FrameSize := MsgSize;
  1026.     FindFrame(FrameSize, FramePos);
  1027.     If SqInfo^.SqBase.LastFrame <> 0 Then
  1028.       Begin
  1029.       ReadVarFrame(TmpFrame, SqInfo^.SqBase.LastFrame);
  1030.       TmpFrame.NextFrame := FramePos;
  1031.       WriteVarFrame(TmpFrame, SqInfo^.SqBase.LastFrame);
  1032.       TmpFrame.PrevFrame := SqInfo^.SqBase.LastFrame;
  1033.       End
  1034.     Else
  1035.       Begin
  1036.       SqInfo^.SqBase.BeginFrame := FramePos;
  1037.       TmpFrame.PrevFrame := 0;
  1038.       End;
  1039.     TmpFrame.Id := SqHdrId;
  1040.     TmpFrame.FrameType := SqFrameMsg;
  1041.     SqInfo^.SqBase.LastFrame := FramePos;
  1042.     TmpFrame.NextFrame := 0;
  1043.     TmpFrame.FrameLength := FrameSize;
  1044.     TmpFrame.MsgLength := MsgSize;
  1045.     TmpFrame.ControlLength := 0;
  1046.     If TmpFrame.FrameLength = 0 Then
  1047.       Begin
  1048.       TmpFrame.FrameLength := TmpFrame.MsgLength + 256; {slack to minimize free frames}
  1049.       SqInfo^.SqBase.EndFrame := FramePos + SqFSize + TmpFrame.FrameLength;
  1050.       End;
  1051.     WriteVarFrame(TmpFrame, FramePos);
  1052.     WriteMsgHdr(FramePos);
  1053.     WriteText(FramePos);
  1054.     Inc(SqInfo^.SqBase.NumMsg);
  1055.     SqIdx^[SqInfo^.SqBase.NumMsg].Ofs := FramePos;
  1056.     SqIdx^[SqInfo^.SqBase.NumMsg].UMsgId := SqInfo^.SqBase.UID;
  1057.     SqIdx^[SqInfo^.SqBase.NumMsg].Hash := SqHashName(Az2Str(SqInfo^.MsgHdr.MsgTo, 35));
  1058.     Inc(SqInfo^.SqBase.UId);
  1059.     SqInfo^.SqBase.HighMsg := SqInfo^.SqBase.NumMsg;
  1060.     KillExcess;
  1061.     SqInfo^.CurrIdx := SqInfo^.SqBase.NumMsg;
  1062.     If Not AlreadyLocked Then
  1063.       If UnLockMsgBase Then;
  1064.     WriteMsg := 0;
  1065.     End
  1066.   Else
  1067.     WriteMsg := 5;
  1068.   End;
  1069.  
  1070.  
  1071. Function SqMsgObj.GetString(MaxLen: Word): String;
  1072.   Var
  1073.     WPos: Word;
  1074.     WLen: Byte;
  1075.     StrDone: Boolean;
  1076.     TxtOver: Boolean;
  1077.     StartSoft: Boolean;
  1078.     CurrLen: Word;
  1079.     PPos: Word;
  1080.     TmpCh: Char;
  1081.  
  1082.   Begin
  1083.   StrDone := False;
  1084.   CurrLen := 0;
  1085.   PPos := SqInfo^.TxtCtr;
  1086.   WPos := 0;
  1087.   WLen := 0;
  1088.   StartSoft := SqInfo^.LastSoft;
  1089.   SqInfo^.LastSoft := True;
  1090.   TmpCh := GetChar;
  1091.   While ((Not StrDone) And (CurrLen < MaxLen) And (Not SqInfo^.MsgDone)) Do
  1092.     Begin
  1093.     Case TmpCh of
  1094.       #$00:;
  1095.       #$0d: Begin
  1096.             StrDone := True;
  1097.             SqInfo^.LastSoft := False;
  1098.             End;
  1099.       #$8d:;
  1100.       #$0a:;
  1101.       #$20: Begin
  1102.             If ((CurrLen <> 0) or (Not StartSoft)) Then
  1103.               Begin
  1104.               Inc(CurrLen);
  1105.               WLen := CurrLen;
  1106.               GetString[CurrLen] := TmpCh;
  1107.               WPos := SqInfo^.TxtCtr;
  1108.               End
  1109.             Else
  1110.               StartSoft := False;
  1111.             End;
  1112.       Else
  1113.         Begin
  1114.         Inc(CurrLen);
  1115.         GetString[CurrLen] := TmpCh;
  1116.         End;
  1117.       End;
  1118.     If Not StrDone Then
  1119.       TmpCh := GetChar;
  1120.     End;
  1121.   If StrDone Then
  1122.     Begin
  1123.     GetString[0] := Chr(CurrLen);
  1124.     End
  1125.   Else
  1126.     If SqInfo^.MsgDone Then
  1127.       Begin
  1128.       GetString[0] := Chr(CurrLen);
  1129.       End
  1130.     Else
  1131.       Begin
  1132.       If WLen = 0 Then
  1133.         Begin
  1134.         GetString[0] := Chr(CurrLen);
  1135.         Dec(SqInfo^.TxtCtr);
  1136.         End
  1137.       Else
  1138.         Begin
  1139.         GetString[0] := Chr(WLen);
  1140.         SqInfo^.TxtCtr := WPos;
  1141.         End;
  1142.       End;
  1143.   End;
  1144.  
  1145.  
  1146. Function SqMsgObj.EOM: Boolean;
  1147.   Begin
  1148.   EOM := SqInfo^.MsgDone;
  1149.   End;
  1150.  
  1151.  
  1152. Function SqMsgObj.WasWrap: Boolean;
  1153.   Begin
  1154.   WasWrap := SqInfo^.LastSoft;
  1155.   End;
  1156.  
  1157.  
  1158. Function SqMsgObj.GetChar: Char;
  1159.   Begin
  1160.   If ((SqInfo^.TxtCtr >= SqInfo^.Frame.MsgLength) Or
  1161.   (SqInfo^.MsgChars[SqInfo^.TxtCtr] = #0)) Then
  1162.     Begin
  1163.     GetChar := #0;
  1164.     SqInfo^.MsgDone := True;
  1165.     End
  1166.   Else
  1167.     Begin
  1168.     GetChar := SqInfo^.MsgChars[SqInfo^.TxtCtr];
  1169.     Inc(SqInfo^.TxtCtr);
  1170.     End;
  1171.   End;
  1172.  
  1173.  
  1174. Function SqMsgObj.GetHighWater: LongInt; {Get high water umsgid}
  1175.   Begin
  1176.   GetHighWater := LongInt(SqInfo^.SqBase.HighWater);
  1177.   End;
  1178.  
  1179.  
  1180. Function SqMsgObj.GetHighMsgNum: LongInt; {Get highest msg number}
  1181.   Begin
  1182.   GetHighMsgNum := LongInt(SqInfo^.SqBase.Uid) - 1;
  1183.   End;
  1184.  
  1185.  
  1186. Procedure SqMsgObj.ReadIdx;
  1187.   Var
  1188.     NumRead: Word;
  1189.  
  1190.   Begin
  1191.   Seek(SqInfo^.SqiFile, 0);
  1192.   If IoResult = 0 Then
  1193.     Begin
  1194.     If Not shRead(SqInfo^.SqiFile, SqIdx^, SqIdxArraySize, NumRead) Then
  1195.       SqInfo^.Error := FileError;
  1196.     End
  1197.   Else
  1198.     SqInfo^.Error := 300;
  1199.   End;
  1200.  
  1201.  
  1202. Procedure SqMsgObj.WriteIdx;
  1203.   Begin
  1204.   Seek(SqInfo^.SqiFile, 0);
  1205.   Truncate(SqInfo^.SqiFile);
  1206.   If IoResult = 0 Then
  1207.     Begin
  1208.     If Not shWrite(SqInfo^.SqiFile, SqIdx^, SqInfo^.SqBase.NumMsg) Then
  1209.       SqInfo^.Error := FileError;
  1210.     End
  1211.   Else
  1212.     SqInfo^.Error := 300;
  1213.   End;
  1214.  
  1215.  
  1216. Procedure SqMsgObj.SeekFirst(MsgNum: LongInt);
  1217.   Begin
  1218.   SqInfo^.CurrIdx := 1;
  1219.   ReadIdx;
  1220.   While ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and
  1221.   (MsgNum > LongInt(SqIdx^[SqInfo^.CurrIdx].UMsgId))) Do
  1222.     SeekNext;
  1223.   End;
  1224.  
  1225.  
  1226. Function SqMsgObj.IdxHighest: LongInt;
  1227.   Var
  1228.     i: Word;
  1229.     Tmp: LongInt;
  1230.  
  1231.   Begin
  1232.   Tmp := 0;
  1233.   i := 1;
  1234.   While i <= SqInfo^.SqBase.NumMsg Do
  1235.     Begin
  1236.     If  SqIdx^[i].UMsgId > Tmp Then
  1237.       Tmp := SqIdx^[i].UMsgId;
  1238.     Inc(i);
  1239.     End;
  1240.   IdxHighest := Tmp;
  1241.   End;
  1242.  
  1243.  
  1244. Function SqMsgObj.GetMsgNum: LongInt;
  1245.   Begin
  1246.   If ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and
  1247.   (SqInfo^.CurrIdx > 0)) Then
  1248.     GetMsgNum := LongInt(SqIdx^[SqInfo^.CurrIdx].UMsgId)
  1249.   Else
  1250.     GetMsgNum := -1;
  1251.   End;
  1252.  
  1253.  
  1254. Procedure SqMsgObj.SeekNext;
  1255.   Begin
  1256.   Inc(SqInfo^.CurrIdx);
  1257.   End;
  1258.  
  1259.  
  1260. Procedure SqMsgObj.SeekPrior;
  1261.   Begin
  1262.   If SqInfo^.CurrIdx > 1 Then
  1263.     Dec(SqInfo^.CurrIdx)
  1264.   Else
  1265.     SqInfo^.CurrIdx := 0;
  1266.   End;
  1267.  
  1268.  
  1269. Function SqMsgObj.SeekFound: Boolean;
  1270.   Begin
  1271.   SeekFound := GetMsgNum >= 0;
  1272.   End;
  1273.  
  1274.  
  1275. Function SqMsgObj.GetIdxFramePos: LongInt;
  1276.   Begin
  1277.   If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then
  1278.     GetIdxFramePos := SqIdx^[SqInfo^.CurrIdx].Ofs
  1279.   Else
  1280.     GetIdxFramePos := -1;
  1281.   End;
  1282.  
  1283.  
  1284. Function SqMsgObj.GetIdxHash: LongInt;
  1285.   Begin
  1286.   If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then
  1287.     GetIdxHash := SqIdx^[SqInfo^.CurrIdx].Hash
  1288.   Else
  1289.     GetIdxHash := 0;
  1290.   End;
  1291.  
  1292.  
  1293. Function SqMsgObj.IsLocal: Boolean; {Is current msg local}
  1294.   Begin
  1295.   IsLocal := ((SqInfo^.MsgHdr.Attr and SqMsgLocal) <> 0);
  1296.   End;
  1297.  
  1298.  
  1299. Function SqMsgObj.IsCrash: Boolean; {Is current msg crash}
  1300.   Begin
  1301.   IsCrash := ((SqInfo^.MsgHdr.Attr and SqMsgCrash) <> 0);
  1302.   End;
  1303.  
  1304.  
  1305. Function SqMsgObj.IsKillSent: Boolean; {Is current msg kill sent}
  1306.   Begin
  1307.   IsKillSent := ((SqInfo^.MsgHdr.Attr and SqMsgKill) <> 0);
  1308.   End;
  1309.  
  1310.  
  1311. Function SqMsgObj.IsSent: Boolean; {Is current msg sent}
  1312.   Begin
  1313.   IsSent := ((SqInfo^.MsgHdr.Attr and SqMsgSent) <> 0);
  1314.   End;
  1315.  
  1316.  
  1317. Function SqMsgObj.IsFAttach: Boolean; {Is current msg file attach}
  1318.   Begin
  1319.   IsFAttach := ((SqInfo^.MsgHdr.Attr and SqMsgFile) <> 0);
  1320.   End;
  1321.  
  1322.  
  1323. Function SqMsgObj.IsReqRct: Boolean; {Is current msg request receipt}
  1324.   Begin
  1325.   IsReqRct := ((SqInfo^.MsgHdr.Attr and SqMsgRRQ) <> 0);
  1326.   End;
  1327.  
  1328.  
  1329. Function SqMsgObj.IsReqAud: Boolean; {Is current msg request audit}
  1330.   Begin
  1331.   IsReqAud := ((SqInfo^.MsgHdr.Attr and SqMsgArq) <> 0);
  1332.   End;
  1333.  
  1334.  
  1335. Function SqMsgObj.IsRetRct: Boolean; {Is current msg a return receipt}
  1336.   Begin
  1337.   IsRetRct := ((SqInfo^.MsgHdr.Attr and SqMsgCpt) <> 0);
  1338.   End;
  1339.  
  1340.  
  1341. Function SqMsgObj.IsFileReq: Boolean; {Is current msg a file request}
  1342.   Begin
  1343.   IsFileReq := ((SqInfo^.MsgHdr.Attr and SqMsgFreq) <> 0);
  1344.   End;
  1345.  
  1346.  
  1347. Function SqMsgObj.IsRcvd: Boolean; {Is current msg received}
  1348.   Begin
  1349.   IsRcvd := ((SqInfo^.MsgHdr.Attr and SqMsgRcvd) <> 0);
  1350.   End;
  1351.  
  1352.  
  1353. Function SqMsgObj.IsPriv: Boolean; {Is current msg priviledged/private}
  1354.   Begin
  1355.   IsPriv := ((SqInfo^.MsgHdr.Attr and SqMsgPriv) <> 0);
  1356.   End;
  1357.  
  1358.  
  1359. Function SqMsgObj.IsEchoed: Boolean;
  1360.   Begin
  1361.   IsEchoed := ((SqInfo^.MsgHdr.Attr and SqMsgScanned) = 0);
  1362.   End;
  1363.  
  1364.  
  1365. Function SqMsgObj.IsDeleted: Boolean; {Is current msg deleted}
  1366.   Begin
  1367.   IsDeleted := False;
  1368.   End;
  1369.  
  1370.  
  1371. Function SqMsgObj.GetRefer: LongInt; {Get reply to of current msg}
  1372.   Begin
  1373.   GetRefer := LongInt(SqInfo^.MsgHdr.ReplyTo);
  1374.   End;
  1375.  
  1376.  
  1377. Procedure SqMsgObj.SetRefer(Num: LongInt); {Set reply to of current msg}
  1378.   Begin
  1379.   SqInfo^.MsgHdr.ReplyTo := LongInt(Num);
  1380.   End;
  1381.  
  1382.  
  1383. Function SqMsgObj.GetSeeAlso: LongInt; {Get see also msg}
  1384.   Begin
  1385.   GetSeeAlso := LongInt(SqInfo^.MsgHdr.Replies[1]);
  1386.   End;
  1387.  
  1388.  
  1389. Procedure SqMsgObj.SetSeeAlso(Num: LongInt); {Set see also msg}
  1390.   Begin
  1391.   SqInfo^.MsgHdr.Replies[1] := LongInt(Num);
  1392.   End;
  1393.  
  1394.  
  1395. Procedure SqMsgObj.SetAttr(St: Boolean; Mask: LongInt); {Set attribute}
  1396.   Begin
  1397.   If St Then
  1398.     SqInfo^.MsgHdr.Attr := SqInfo^.MsgHdr.Attr or Mask
  1399.   Else
  1400.     SqInfo^.MsgHdr.Attr := SqInfo^.MsgHdr.Attr and (Not Mask);
  1401.   End;
  1402.  
  1403.  
  1404. Procedure SqMsgObj.SetLocal(St: Boolean); {Set local status}
  1405.   Begin
  1406.   SetAttr(St, SqMsgLocal);
  1407.   End;
  1408.  
  1409.  
  1410. Procedure SqMsgObj.SetRcvd(St: Boolean); {Set received status}
  1411.   Begin
  1412.   SetAttr(St, SqMsgRcvd);
  1413.   End;
  1414.  
  1415.  
  1416. Procedure SqMsgObj.SetPriv(St: Boolean); {Set priveledge vs public status}
  1417.   Begin
  1418.   SetAttr(St, SqMsgPriv);
  1419.   End;
  1420.  
  1421.  
  1422. Procedure SqMsgObj.SetEcho(ES: Boolean);
  1423.   Begin
  1424.   SetAttr(Not ES, SqMsgScanned);
  1425.   End;
  1426.  
  1427.  
  1428. Procedure SqMsgObj.SetCrash(St: Boolean); {Set crash netmail status}
  1429.   Begin
  1430.   SetAttr(St, SqMsgCrash);
  1431.   End;
  1432.  
  1433.  
  1434. Procedure SqMsgObj.SetKillSent(St: Boolean); {Set kill/sent netmail status}
  1435.   Begin
  1436.   SetAttr(St, SqMsgKill);
  1437.   End;
  1438.  
  1439.  
  1440. Procedure SqMsgObj.SetSent(St: Boolean); {Set sent netmail status}
  1441.   Begin
  1442.   SetAttr(St, SqMsgSent);
  1443.   End;
  1444.  
  1445.  
  1446. Procedure SqMsgObj.SetFAttach(St: Boolean); {Set file attach status}
  1447.   Begin
  1448.   SetAttr(St, SqMsgFile);
  1449.   End;
  1450.  
  1451.  
  1452. Procedure SqMsgObj.SetReqRct(St: Boolean); {Set request receipt status}
  1453.   Begin
  1454.   SetAttr(St, SqMsgRrq);
  1455.   End;
  1456.  
  1457.  
  1458. Procedure SqMsgObj.SetReqAud(St: Boolean); {Set request audit status}
  1459.   Begin
  1460.   SetAttr(St, SqMsgarq);
  1461.   End;
  1462.  
  1463.  
  1464. Procedure SqMsgObj.SetRetRct(St: Boolean); {Set return receipt status}
  1465.   Begin
  1466.   SetAttr(St, SqMsgCpt);
  1467.   End;
  1468.  
  1469.  
  1470. Procedure SqMsgObj.SetFileReq(St: Boolean); {Set file request status}
  1471.   Begin
  1472.   SetAttr(St, SqMsgFreq);
  1473.   End;
  1474.  
  1475.  
  1476. Procedure SqMsgObj.MsgStartUp;
  1477.   Begin
  1478.   SqInfo^.CurrentFramePos := GetIdxFramePos;
  1479.   SqInfo^.CurrentUID := SqIdx^[SqInfo^.CurrIdx].UMsgId;
  1480.   ReadFrame(SqInfo^.CurrentFramePos);
  1481.   ReadMsgHdr(SqInfo^.CurrentFramePos);
  1482.   End;
  1483.  
  1484.  
  1485. Procedure SqMsgObj.MsgTxtStartUp;
  1486.   Var
  1487.     CFrame: LongInt;
  1488.  
  1489.   Begin
  1490.   ReadText(SqInfo^.CurrentFramePos);
  1491.   End;
  1492.  
  1493.  
  1494. Procedure SqMsgObj.SetMailType(MT: MsgMailType);
  1495.   Begin
  1496.   End;
  1497.  
  1498.  
  1499. Function SqMsgObj.GetSubArea: Word;
  1500.   Begin
  1501.   GetSubArea := 0;
  1502.   End;
  1503.  
  1504.  
  1505. Procedure SqMsgObj.ReWriteHdr;
  1506.   Var
  1507.     AlreadyLocked: Boolean;
  1508.     i: LongInt;
  1509.  
  1510.  
  1511.   Begin
  1512.   AlreadyLocked := SqInfo^.Locked;
  1513.   If Not AlreadyLocked Then
  1514.     If LockMsgBase Then;
  1515.   WriteFrame(SqInfo^.CurrentFramePos);
  1516.   WriteMsgHdr(SqInfo^.CurrentFramePos);
  1517.   i := 1;
  1518.   While ((i <= SqInfo^.SqBase.NumMsg) and (SqInfo^.CurrentFramePos <> SqIdx^[i].Ofs)) Do
  1519.     Inc(i);
  1520.   If SqIdx^[i].Ofs = SqInfo^.CurrentFramePos Then
  1521.     Begin
  1522.     If IsRcvd Then
  1523.       SqIdx^[i].Hash := 0
  1524.     Else
  1525.       SqIdx^[i].Hash := SqHashName(SqInfo^.MsgHdr.MsgTo);
  1526.     End;
  1527.   If Not AlreadyLocked Then
  1528.     If UnLockMsgBase Then;
  1529.   End;
  1530.  
  1531.  
  1532. Procedure SqMsgObj.DeleteMsg;
  1533.   Begin
  1534.   KillMsg(SqInfo^.CurrentUID);
  1535.   End;
  1536.  
  1537.  
  1538. Function SqMsgObj.NumberOfMsgs: LongInt;
  1539.   Var
  1540.     TmpBase: SqBaseType;
  1541.  
  1542.   Begin
  1543.   If LoadFile(SqInfo^.FN + '.Sqd', TmpBase, SizeOf(TmpBase)) = 0 Then
  1544.     NumberOfMsgs := TmpBase.NumMsg
  1545.   Else
  1546.     NumberOfMsgs := 0;
  1547.   End;
  1548.  
  1549.  
  1550. Function SqMsgObj.GetLastRead(UNum: LongInt): LongInt;
  1551.   Var
  1552.     LRec: LongInt;
  1553.  
  1554.   Begin
  1555.   If ((UNum + 1) * SizeOf(LRec)) >
  1556.   SizeFile(SqInfo^.FN + '.Sql') Then
  1557.     GetLastRead := 0
  1558.   Else
  1559.     Begin
  1560.     If LoadFilePos(SqInfo^.FN + '.Sql', LRec, SizeOf(LRec),
  1561.     UNum * SizeOf(LRec)) = 0 Then
  1562.       GetLastRead := LRec
  1563.     Else
  1564.       GetLastRead := 0;
  1565.     End;
  1566.   End;
  1567.  
  1568.  
  1569. Procedure SqMsgObj.SetLastRead(UNum: LongInt; LR: LongInt);
  1570.   Var
  1571.     LRec: LongInt;
  1572.     Status: Word;
  1573.  
  1574.   Begin
  1575.   Status := 0;
  1576.   If ((UNum + 1) * SizeOf(LRec)) >
  1577.   SizeFile(SqInfo^.FN + '.Sql') Then
  1578.     Begin
  1579.     Status := ExtendFile(SqInfo^.FN + '.Sql', (UNum + 1) * SizeOf(LRec));
  1580.     End;
  1581.   LRec := LR;
  1582.   If Status = 0 Then
  1583.     Status := SaveFilePos(SqInfo^.FN + '.Sql', LRec, SizeOf(LRec),
  1584.       UNum * SizeOf(LRec));
  1585.   End;
  1586.  
  1587.  
  1588. Function SqMsgObj.GetMsgLoc: LongInt;
  1589.   Begin
  1590.   GetMsgLoc := GetMsgNum;
  1591.   End;
  1592.  
  1593.  
  1594. Procedure SqMsgObj.SetMsgLoc(ML: LongInt);
  1595.   Begin
  1596.   SeekFirst(ML);
  1597.   End;
  1598.  
  1599.  
  1600. Procedure SqMsgObj.YoursFirst(Name: String; Handle: String);
  1601.   Begin
  1602.   SqInfo^.CurrIdx := 0;
  1603.   ReadIdx;
  1604.   SqInfo^.SName := Upper(Name);
  1605.   SqInfo^.SHandle := Upper(Handle);
  1606.   SqInfo^.HName := SqHashName(Name);
  1607.   SqInfo^.HHandle := SqHashName(Handle);
  1608.   YoursNext;
  1609.   End;
  1610.  
  1611.  
  1612. Procedure SqMsgObj.YoursNext;
  1613.   Var
  1614.     WasFound: Boolean;
  1615.  
  1616.   Begin
  1617.   WasFound := False;
  1618.   Inc(SqInfo^.CurrIdx);
  1619.   While ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and (Not WasFound)) Do
  1620.     Begin
  1621.     While ((SqIdx^[SqInfo^.CurrIdx].Hash <> SqInfo^.HName) And
  1622.     (SqIdx^[SqInfo^.CurrIdx].Hash <> SqInfo^.HHandle) And
  1623.     (SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg)) Do
  1624.       Inc(SqInfo^.CurrIdx);
  1625.     If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then
  1626.       Begin
  1627.       MsgStartUp;
  1628.       If ((Not IsRcvd) and
  1629.       ((Upper(GetTo) = SqInfo^.SName) or (Upper(GetTo) = SqInfo^.SHandle))) Then
  1630.         WasFound := True
  1631.       Else
  1632.         Inc(SqInfo^.CurrIdx);
  1633.       End;
  1634.     End;
  1635.   End;
  1636.  
  1637.  
  1638. Function SqMsgObj.YoursFound: Boolean;
  1639.   Begin
  1640.   YoursFound := SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg;
  1641.   End;
  1642.  
  1643.  
  1644. Function SqMsgObj.GetMsgDisplayNum: LongInt;
  1645.   Begin
  1646.   GetMsgDisplayNum := SqInfo^.CurrIdx;
  1647.   End;
  1648.  
  1649.  
  1650. Function SqMsgObj.GetTxtPos: LongInt;
  1651.   Begin
  1652.   GetTxtPos := SqInfo^.TxtCtr;
  1653.   End;
  1654.  
  1655.  
  1656. Procedure SqMsgObj.SetTxtPos(TP: LongInt);
  1657.   Begin
  1658.   SqInfo^.TxtCtr := TP;
  1659.   End;
  1660.  
  1661.  
  1662. End.
  1663.